
// Copyright (C) 2009-2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;

using Derm.Render;
using Derm.Xml.Collada;

namespace Derm.Scene.Mesh
{
	/// <summary>
	/// Complex geometry.
	/// </summary>
	/// <remarks>
	/// <para>
	/// Simple geometry can be rendered simply deriving a RenderGraphNode class and setup
	/// an appropriate shader program, its inputs and its uniform state. But for complex
	/// geometries with multiple primitives types, with normals, multiple texture coordinates,
	/// colors and with many other attributes could be hard to handle.
	/// </para>
	/// <para>
	/// The Mesh class aids the geometry rendering, allowing to subdivide the complex geometry
	/// into simpler surfaces.
	/// 
	/// Every surface, for being part of the same Mesh instance, shall have the same vertices attributes
	/// set. It is an error to supply a Surface not coherent with other already defined surfaces.
	/// This restriction is necessary to avoid a dedicated ShaderProgram for each surface (and consequent
	/// shader input updates). This limitation could be arounded using appropriate render hierarchy, or
	/// introducing conditional code in the ShaderPrograms and setup uniform state of specific surfaces.
	/// </para>
	/// </remarks>
	public class Mesh : RenderObject
	{
		#region Constructors
		
		/// <summary>
		/// Mesh constructor.
		/// </summary>
		public Mesh()
		{
			
		}

		#endregion

		#region Vertex Arrays Management

		#endregion

		#region Normals Management

		/// <summary>
		/// Enumeration indicating use vertices normal.
		/// </summary>
		protected enum NormalMode
		{
			/// <summary>
			/// Vertices normals are not set. 
			/// </summary>
			Disabled,
			/// <summary>
			/// Vertices normals are set, but are not used. 
			/// </summary>
			VertexButDisabled,
			/// <summary>
			/// Each vertex has a normal vector. 
			/// </summary>
			Vertex
		}

		/// <summary>
		/// Invert all normals of this Mesh.
		/// </summary>
		public void FlipNormals()
		{
			//foreach (Surface s in mSurfaces) {
			//	ArrayBufferObject<Vertex3f> nbo = s.Attributes.NormalBuffer;

			//	if (nbo != null) {
			//		for (int i = 0; i < nbo.ItemsCount; i++) {
			//			Vertex3f n = nbo[i];

			//			n.x *= -1.0f; n.y *= -1.0f; n.z *= -1.0f;

			//			nbo[i] = n;
			//		}
			//	}
			//}
		}
		
		/// <summary>
		/// Disable mesh normals. 
		/// </summary>
		/// <remarks>
		/// It deallocates normal buffers if it's actually present.
		/// </remarks>
		public void DisableNormals()
		{
			mNormalMode = NormalMode.Disabled;
		}
		
		/// <summary>
		/// Temporarly disable mesh normals. 
		/// </summary>
		/// <param name="on">
		/// A <see cref="System.Boolean"/> indicating whether the mesh normals
		/// are send to the graphic system, without deallocating normal buffers.
		/// </param>
		public void DisableNormals(bool on)
		{
			
		}
		
		/// <summary>
		/// Automatically compute mesh normals.
		/// </summary>
		public void ComputeNormals()
		{
			//if (rBuffers == RenderBuffers.Buffer.None) {
			//	foreach (Surface s in mSurfaces) {
			//		int comp = 3;

			//		vbo = s.Attributes.VertexBuffer;
			//		nbo = s.Attributes.NormalBuffer;

			//		for (int i = 0; i < vbo.ItemsCount; i+= comp) {
			//			// Compute normal
			//			n = ((vbo[i+0]-vbo[i+1]) % (vbo[i+0]-vbo[i+2]));
			//			n.Normalize();
			//			// Store normal
			//			for (int j = i; j < i+comp; j++)
			//				nbo[j] = n;
			//		}
			//		s.Attributes.SetNormalBuffer(nbo);
			//	}
			//	// Enable normal buffer
			//	mNormalMode = NormalMode.Vertex;
			//} else {
			//	int aIdx = 0, bIdx = 0, cIdx = 0;
				
			//	vbo = mPackedDataArrays.VertexBuffer;
			//	// Reallocate normal buffer
			//	nbo = new ArrayBufferObject<Vertex3f>();
			//	nbo.AllocateOnClient(mPackedDataArrays.VertexBuffer.ItemsCount);
			//	// Compute normal vectors
			//	foreach (Surface s in mSurfaces) {
			//		ElementBufferObject<uint> mIndices = s.mDataArrays.ElementBuffer;
			//		for (int i = 0; i < mIndices.ItemsCount;) {
			//			switch (s.mPrimitive) {
			//			case SurfaceType.Triangle:
			//				// Retrieve plane vertices
			//				aIdx = mIndices[i++];
			//				bIdx = mIndices[i++];
			//				cIdx = mIndices[i++];
			//				Debug.Assert(i <= mIndices.ItemsCount);
			//				break;
			//			case SurfaceType.TriangleStrip:
			//				aIdx = mIndices[i++];
			//				bIdx = mIndices[i++];
			//				cIdx = mIndices[i++];
			//				Debug.Assert(i < mIndices.ItemsCount);
			//				break;
			//			case SurfaceType.TriangleFan:
			//				aIdx = mIndices[i++];
			//				bIdx = mIndices[i++];
			//				cIdx = mIndices[i++];
			//				Debug.Assert(i < mIndices.ItemsCount);
			//				break;
			//			//case SurfaceType.Quad:
			//			//	aIdx = mIndices[i++];
			//			//	bIdx = mIndices[i++];
			//			//	cIdx = mIndices[i++];
			//			//	i++;	// Ignore 4th vertex
			//			//	Debug.Assert(i < mIndices.ItemsCount);
			//			//	break;
			//			//case SurfaceType.QuadStrip:
			//			//	aIdx = mIndices[i++];
			//			//	bIdx = mIndices[i++];
			//			//	cIdx = mIndices[i++];
			//			//	Debug.Assert(i < mIndices.ItemsCount);
			//			//	break;
			//			}
			//		}

			//		a = vbo[aIdx];
			//		b = vbo[bIdx];
			//		c = vbo[cIdx];

			//		n = ((a-b) % (a-c));
			//		n.Normalize();

			//		// Sum normal vectors (smooth normals)
			//		nbo[aIdx] += n;
			//		nbo[bIdx] += n;
			//		nbo[cIdx] += n;
			//	}
			//	// Enable normal buffer
			//	mNormalMode = NormalMode.Vertex;

			//	mPackedDataArrays.SetNormalBuffer(nbo);
			//}
		}
		
		/// <summary>
		/// 
		/// </summary>
		private NormalMode mNormalMode = NormalMode.Disabled;
		
		#endregion

		#region Bounding Box Management

		/// <summary>
		/// Compute mesh bounds.
		/// </summary>
		public void ComputeBounds()
		{
			//BoundBox bVolume = new BoundBox();

			//// Compute bounds of packed buffer
			//if ((mPackedDataArrays.RequiredBuffers & RenderBuffers.Buffer.Vertex) != 0)
			//	bVolume.ComputeBounds(mPackedDataArrays.VertexBuffer);
			//// Compute bound of each surface
			//foreach (Surface s in mSurfaces) {
			//	if ((s.Attributes.RequiredBuffers & RenderBuffers.Buffer.Vertex) != 0)
			//		bVolume.ComputeBounds(mPackedDataArrays.VertexBuffer);
			//}
			//// Update bounds
			//Bounds = bVolume;
		}

		/// <summary>
		/// Bound volume of this Mesh.
		/// </summary>
		public IBoundVolume Bounds
		{
			get { return (mBoundVolume); }
			set { mBoundVolume = value; }
		}

		/// <summary>
		/// Volume best representing this mesh.
		/// </summary>
		private IBoundVolume mBoundVolume = null;

		#endregion
	}
}
